Exploring Global Temperature in the ERA of Climate Change¶
Puoi trovare il codice a questo profilo github Github - Global-2mTemperature-Analysis
Veronica Grazia Morelli - 839257¶
Il cambiamento climatico rappresenta una sfida formidabile per il nostro pianeta, influenzando in particolare i modelli di temperatura e esercitando profonde influenze su vari aspetti ecologici ed atmosferici. Nel campo dell'analisi delle temperature, la temperatura superficiale si distingue come una variabile chiave, offrendo importanti approfondimenti sulla dinamica climatica.
Le variazioni della temperatura superficiale influenzano una moltitudine di processi ambientali, compresa la distribuzione del calore, il trasferimento di energia e la frequenza di ondate di calore o periodi di freddo. Prevedere e comprendere le alterazioni nella temperatura superficiale ĆØ cruciale per adattarsi alle mutevoli condizioni climatiche e prevedere l'insorgenza di eventi estremi di caldo o freddo.
Questa ricerca esamina i dati storici sulla temperatura superficiale e utilizza modelli climatici per svelare sottili cambiamenti nei modelli di temperatura nel corso di diverse decadi. Analisi temporali e spaziali verranno applicate ai dati sulla temperatura superficiale per identificare tendenze, anomalie e variazioni nel tempo.
Inoltre, il progetto prevede confronti tra i dati osservati storici sulla temperatura superficiale e le simulazioni generate da un modello climatico, fornendo una prospettiva completa sulla dinamica dei cambiamenti di temperatura nel periodo studiato.
ERA5 2m Temperature¶
In questo studio, utilizzo le misurazioni mensili della temperatura a 2 metri fornite dal dataset ERA5. ERA5, sviluppato dal Centro Europeo per le Previsioni Meteorologiche a Medio Termine (ECMWF), offre un'importante prospettiva sulla natura dinamica del sistema climatico terrestre. A causa di vincoli computazionali, mi sono concentrato sulle misurazioni mensili, consentendo di catturare le tendenze più ampie e le variazioni nella temperatura nel corso del tempo.
Ho scaricato le misurazioni mensili della temperatura superficiale dal seguente sito web: Copernicus Climate Data Store - ERA5 Single Level Data.
MPI-ESM1-2-LR in SSP126 scenario¶
Il modello MPI-ESM1-2-LR ĆØ un modello di sistema terrestre all'avanguardia sviluppato dall'Istituto Max Planck per la Meteorologia (MPI-M). Appartiene alla famiglia di modelli conosciuta come Max Planck Institute Earth System Model (MPI-ESM). Questo modello ĆØ progettato per simulare vari componenti del sistema climatico della Terra, tra cui l'atmosfera, gli oceani, la superficie terrestre e il ghiaccio marino. Le sue capacitĆ lo rendono uno strumento prezioso per lo studio della dinamica climatica, la comprensione degli impatti delle attivitĆ umane e la formulazione di proiezioni per scenari climatici futuri.
Lo scenario SSP126 (Shared Socioeconomic Pathway 126) ĆØ uno dei Percorsi Socioeconomici Condivisi sviluppati dal Panel Intergovernativo sui Cambiamenti Climatici (IPCC). SSP126 rappresenta un mondo futuro in cui lo sviluppo sostenibile ĆØ una prioritĆ , portando a basse emissioni di gas serra. In questo scenario, sforzi internazionali coordinati portano a una significativa riduzione delle emissioni, mitigando gli impatti dei cambiamenti climatici. Serve come base per lo studio di un futuro in cui la sostenibilitĆ ambientale e l'equitĆ sociale sono enfatizzate.
La selezione del modello MPI-ESM1-2-LR sotto lo scenario SSP126 (2015-2050) per l'analisi fornisce una prospettiva su una traiettoria climatica futura con emissioni relativamente basse e un'attenzione alle pratiche sostenibili. Questa scelta consente di esplorare possibili esiti climatici in scenari che privilegiano la gestione ambientale e la cooperazione globale.
Ho scaricato le misurazioni mensili della temperatura superficiale dal seguente sito web: Copernicus Climate Data Store - CMIP6 Projections.
Environment Setup¶
# Import libraries
import json
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import ticker
import matplotlib.dates as mdates
from tqdm import tqdm
from datetime import datetime
import warnings
# import cv2
# import xesmf as xe
import xarray as xr
import cartopy.crs as ccrs
import cartopy.feature as cf
import geopandas as gpd
import cftime
from scipy.stats import stats
# To make visualization better
%matplotlib inline
%config InlineBackend.figure_format='retina'
# Set plot size
plt.rcParams['figure.figsize'] = [8, 4]
# To ignore all warnings
warnings.filterwarnings("ignore")
Data Import e Visualizzazioni¶
ERA5 Temperature (monthly, 1950-2022)¶
Il dataset ERA5 fornisce informazioni sulla pressione atmosferica ad alta risoluzione a livello giornaliero dal 1940 fino ad oggi. Prodotta dal Centro Europeo per le Previsioni Meteorologiche a Medio Termine (ECMWF), ERA5 rappresenta un dataset di rianalisi all'avanguardia che combina dati osservazionali con tecniche di modellizzazione avanzate.
Le misurazioni mensili della temperatura a 2 metri (temperatura superficiale) da gennaio 1950 a dicembre 2022 sono state scaricate. Le misurazioni giornaliere della temperatura erano troppo pesanti per essere supportate dal mio computer.
Puoi trovare il codice nello script api/api_monthly_temperature.py. In totale ci sono 886 misurazioni ($73 \cdot 12 = 876$).
path1_temp = 'Dataset/download_temperature_50_84.nc'
temp1 = xr.open_dataset(path1_temp)
temp1
<xarray.Dataset>
Dimensions: (longitude: 1440, latitude: 721, time: 420)
Coordinates:
* longitude (longitude) float32 0.0 0.25 0.5 0.75 ... 359.0 359.2 359.5 359.8
* latitude (latitude) float32 90.0 89.75 89.5 89.25 ... -89.5 -89.75 -90.0
* time (time) datetime64[ns] 1950-01-01 1950-02-01 ... 1984-12-01
Data variables:
t2m (time, latitude, longitude) float32 ...
Attributes:
Conventions: CF-1.6
history: 2023-12-06 15:35:43 GMT by grib_to_netcdf-2.25.1: /opt/ecmw...temp1.time[0].values, temp1.time[-1].values
(numpy.datetime64('1950-01-01T00:00:00.000000000'),
numpy.datetime64('1984-12-01T00:00:00.000000000'))
path2_temp = 'Dataset/download_temperature_85_23.nc'
temp2 = xr.open_dataset(path2_temp)
temp2
<xarray.Dataset>
Dimensions: (longitude: 1440, latitude: 721, time: 456)
Coordinates:
* longitude (longitude) float32 0.0 0.25 0.5 0.75 ... 359.0 359.2 359.5 359.8
* latitude (latitude) float32 90.0 89.75 89.5 89.25 ... -89.5 -89.75 -90.0
* time (time) datetime64[ns] 1985-01-01 1985-02-01 ... 2022-12-01
Data variables:
t2m (time, latitude, longitude) float32 ...
Attributes:
Conventions: CF-1.6
history: 2023-12-06 15:24:22 GMT by grib_to_netcdf-2.25.1: /opt/ecmw...temp2.time[0].values, temp2.time[-1].values
(numpy.datetime64('1985-01-01T00:00:00.000000000'),
numpy.datetime64('2022-12-01T00:00:00.000000000'))
Il dataset ha una dimensione di 1440 punti in longitudine e 721 punti in latitudine. La risoluzione spaziale approssimativa ĆØ calcolata come segue:
- Risoluzione in latitudine = 360 gradi / 721 ā 0.4993 gradi
- Risoluzione in longitudine = 360 gradi / 1440 ā 0.25 gradi
Pertanto, la risoluzione spaziale approssimativa per il tuo dataset ĆØ di circa 0.4993 gradi in latitudine e 0.25 gradi in longitudine.
Concatenation
temp = xr.concat([temp1, temp2], dim='time')
temp
<xarray.Dataset>
Dimensions: (longitude: 1440, latitude: 721, time: 876)
Coordinates:
* longitude (longitude) float32 0.0 0.25 0.5 0.75 ... 359.0 359.2 359.5 359.8
* latitude (latitude) float32 90.0 89.75 89.5 89.25 ... -89.5 -89.75 -90.0
* time (time) datetime64[ns] 1950-01-01 1950-02-01 ... 2022-12-01
Data variables:
t2m (time, latitude, longitude) float32 241.5 241.5 ... 244.5 244.5
Attributes:
Conventions: CF-1.6
history: 2023-12-06 15:35:43 GMT by grib_to_netcdf-2.25.1: /opt/ecmw...temp.time[0].values, temp.time[-1].values
(numpy.datetime64('1950-01-01T00:00:00.000000000'),
numpy.datetime64('2022-12-01T00:00:00.000000000'))
# Save
temp.to_netcdf('Dataset/temperature.nc')
Longitude Conversion¶
Da questa uscita noto che le coordinate di latitudine e longitudine si riferiscono al baricentro delle celle, ma l'intervallo di longitudine ĆØ [0, 359]. Ho convertito le coordinate di longitudine nell'intervallo [-180, 179].
La ragione di questo adeguamento è portare i valori di longitudine a un intervallo standard più comunemente utilizzato nelle scienze geografiche e nelle applicazioni GIS.
Nel dataset originale ERA5, i valori di longitudine sono forniti nell'intervallo [0, 359], dove 0 rappresenta il meridiano di Greenwich e i valori aumentano verso est fino a raggiungere 359. Questa convenzione è talvolta utilizzata nei modelli climatici e nei dati meteorologici. Tuttavia, è più comune in molte applicazioni geospaziali rappresentare le longitudini nell'intervallo [-180, 180], dove i valori negativi rappresentano ovest del meridiano di Greenwich e i valori positivi rappresentano est.
La conversione delle longitudini nell'intervallo [-180, 180] semplifica il lavoro con i dati, specialmente quando si combinano set di dati, si eseguono mappe o analisi spaziali. Si allinea allo standard utilizzato in molte librerie e strumenti GIS.
# Change longitude definition from [0, 360] to [-180, 180]
temp['longitude_bnds'] = (temp.longitude + 180) % 360 - 180 # boundaries
temp = temp.assign_coords(longitude=temp.longitude_bnds).sortby('longitude') # coordinates
temp = temp.sortby('latitude', ascending=False) # sort by lat
temp
<xarray.Dataset>
Dimensions: (longitude: 1440, latitude: 721, time: 876)
Coordinates:
* longitude (longitude) float32 -180.0 -179.8 -179.5 ... 179.5 179.8
* latitude (latitude) float32 90.0 89.75 89.5 ... -89.5 -89.75 -90.0
* time (time) datetime64[ns] 1950-01-01 1950-02-01 ... 2022-12-01
Data variables:
t2m (time, latitude, longitude) float32 241.5 241.5 ... 244.5
longitude_bnds (longitude) float32 -180.0 -179.8 -179.5 ... 179.5 179.8
Attributes:
Conventions: CF-1.6
history: 2023-12-06 15:35:43 GMT by grib_to_netcdf-2.25.1: /opt/ecmw...Alcune Analisi con Xarray¶
print("Dimensions and Sizes:")
print(temp.dims)
print('---------------------------')
print("Coordinates:")
print(temp.coords)
print('---------------------------')
print("Attributes:")
print(temp.attrs)
print('---------------------------')
print(temp.data_vars)
Dimensions and Sizes:
Frozen({'longitude': 1440, 'latitude': 721, 'time': 876})
---------------------------
Coordinates:
Coordinates:
* longitude (longitude) float32 -180.0 -179.8 -179.5 ... 179.2 179.5 179.8
* latitude (latitude) float32 90.0 89.75 89.5 89.25 ... -89.5 -89.75 -90.0
* time (time) datetime64[ns] 1950-01-01 1950-02-01 ... 2022-12-01
---------------------------
Attributes:
{'Conventions': 'CF-1.6', 'history': '2023-12-06 15:35:43 GMT by grib_to_netcdf-2.25.1: /opt/ecmwf/mars-client/bin/grib_to_netcdf.bin -S param -o /cache/data6/adaptor.mars.internal-1701876927.5603483-31998-9-b7c970fe-2f51-4d29-a310-d353ee49ca37.nc /cache/tmp/b7c970fe-2f51-4d29-a310-d353ee49ca37-adaptor.mars.internal-1701876809.7885528-31998-16-tmp.grib'}
---------------------------
Data variables:
t2m (time, latitude, longitude) float32 241.5 241.5 ... 244.5
longitude_bnds (longitude) float32 -180.0 -179.8 -179.5 ... 179.5 179.8
print(temp.t2m.coords)
Coordinates: * longitude (longitude) float32 -180.0 -179.8 -179.5 ... 179.2 179.5 179.8 * latitude (latitude) float32 90.0 89.75 89.5 89.25 ... -89.5 -89.75 -90.0 * time (time) datetime64[ns] 1950-01-01 1950-02-01 ... 2022-12-01
temp.t2m[0].coords['time']
<xarray.DataArray 'time' ()>
array('1950-01-01T00:00:00.000000000', dtype='datetime64[ns]')
Coordinates:
time datetime64[ns] 1950-01-01
Attributes:
long_name: timeMPI-ESM1-2-LR ssp126 (2015-2050)¶
path_model = 'Dataset/MPI-ESM1-2-LR/ts_Amon_MPI-ESM1-2-LR_ssp126_r1i1p1f1_gn_20150116-20501216_v20190710.nc'
model = xr.open_dataset(path_model)
model
<xarray.Dataset>
Dimensions: (time: 432, bnds: 2, lat: 96, lon: 192)
Coordinates:
* time (time) datetime64[ns] 2015-01-16T12:00:00 ... 2050-12-16T12:00:00
* lat (lat) float64 -88.57 -86.72 -84.86 -83.0 ... 84.86 86.72 88.57
* lon (lon) float64 0.0 1.875 3.75 5.625 ... 352.5 354.4 356.2 358.1
Dimensions without coordinates: bnds
Data variables:
time_bnds (time, bnds) datetime64[ns] ...
lat_bnds (time, lat, bnds) float64 ...
lon_bnds (time, lon, bnds) float64 ...
ts (time, lat, lon) float32 ...
Attributes: (12/47)
Conventions: CF-1.7 CMIP-6.2
activity_id: ScenarioMIP
branch_method: standard
branch_time_in_child: 60265.0
branch_time_in_parent: 60265.0
contact: cmip6-mpi-esm@dkrz.de
... ...
title: MPI-ESM1-2-LR output prepared for CMIP6
variable_id: ts
variant_label: r1i1p1f1
license: CMIP6 model data produced by MPI-M is licensed un...
cmor_version: 3.5.0
tracking_id: hdl:21.14100/47b33dbd-7fcd-4f09-abcf-25202400ba8cmodel.time[0].values, model.time[-1].values
(numpy.datetime64('2015-01-16T12:00:00.000000000'),
numpy.datetime64('2050-12-16T12:00:00.000000000'))
Longitude Conversion¶
Dall'output noto che le coordinate di latitudine e longitudine si riferiscono al baricentro delle celle, ma l'intervallo di longitudine ĆØ [0, 359]. Ho convertito le coordinate di longitudine nell'intervallo [-180, 179]. Ho applicato la stessa operazione effettuata per la temperatura ERA5.
# Change longitude definition from [0, 360] to [-180, 180] for the model
model['lon_bnds'] = (model.lon_bnds + 180) % 360 - 180 # boundaries
model = model.assign_coords(lon=(model.lon + 180) % 360 - 180).sortby('lon') # coordinates
model = model.sortby('lat', ascending=False) # sort by lat
model
<xarray.Dataset>
Dimensions: (time: 432, bnds: 2, lat: 96, lon: 192)
Coordinates:
* time (time) datetime64[ns] 2015-01-16T12:00:00 ... 2050-12-16T12:00:00
* lat (lat) float64 88.57 86.72 84.86 83.0 ... -84.86 -86.72 -88.57
* lon (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
Dimensions without coordinates: bnds
Data variables:
time_bnds (time, bnds) datetime64[ns] ...
lat_bnds (time, lat, bnds) float64 ...
lon_bnds (time, lon, bnds) float64 179.1 -179.1 -179.1 ... 177.2 179.1
ts (time, lat, lon) float32 ...
Attributes: (12/47)
Conventions: CF-1.7 CMIP-6.2
activity_id: ScenarioMIP
branch_method: standard
branch_time_in_child: 60265.0
branch_time_in_parent: 60265.0
contact: cmip6-mpi-esm@dkrz.de
... ...
title: MPI-ESM1-2-LR output prepared for CMIP6
variable_id: ts
variant_label: r1i1p1f1
license: CMIP6 model data produced by MPI-M is licensed un...
cmor_version: 3.5.0
tracking_id: hdl:21.14100/47b33dbd-7fcd-4f09-abcf-25202400ba8cAlcune Analisi con Xarray¶
print("Dimensions and Sizes:")
print(model.dims)
print('---------------------------')
print("Coordinates:")
print(model.coords)
print('---------------------------')
print("Attributes:")
print(model.attrs)
print('---------------------------')
print(model.data_vars)
Dimensions and Sizes:
Frozen({'time': 432, 'bnds': 2, 'lat': 96, 'lon': 192})
---------------------------
Coordinates:
Coordinates:
* time (time) datetime64[ns] 2015-01-16T12:00:00 ... 2050-12-16T12:00:00
* lat (lat) float64 88.57 86.72 84.86 83.0 ... -83.0 -84.86 -86.72 -88.57
* lon (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
---------------------------
Attributes:
{'Conventions': 'CF-1.7 CMIP-6.2', 'activity_id': 'ScenarioMIP', 'branch_method': 'standard', 'branch_time_in_child': 60265.0, 'branch_time_in_parent': 60265.0, 'contact': 'cmip6-mpi-esm@dkrz.de', 'creation_date': '2019-10-29T16:19:11Z', 'data_specs_version': '01.00.30', 'experiment': 'update of RCP2.6 based on SSP1', 'experiment_id': 'ssp126', 'external_variables': 'areacella', 'forcing_index': 1, 'frequency': 'mon', 'further_info_url': 'https://furtherinfo.es-doc.org/CMIP6.MPI-M.MPI-ESM1-2-LR.ssp126.none.r1i1p1f1', 'grid': 'gn', 'grid_label': 'gn', 'history': '2019-10-29T16:19:11Z ; CMOR rewrote data to be consistent with CMIP6, CF-1.7 CMIP-6.2 and CF standards.', 'initialization_index': 1, 'institution': 'Max Planck Institute for Meteorology, Hamburg 20146, Germany', 'institution_id': 'MPI-M', 'mip_era': 'CMIP6', 'nominal_resolution': '250 km', 'parent_activity_id': 'CMIP', 'parent_experiment_id': 'historical', 'parent_mip_era': 'CMIP6', 'parent_source_id': 'MPI-ESM1-2-LR', 'parent_time_units': 'days since 1850-1-1 00:00:00', 'parent_variant_label': 'r1i1p1f1', 'physics_index': 1, 'product': 'model-output', 'project_id': 'CMIP6', 'realization_index': 1, 'realm': 'atmos', 'references': 'MPI-ESM: Mauritsen, T. et al. (2019), Developments in the MPIāM Earth System Model version 1.2 (MPIāESM1.2) and Its Response to Increasing CO2, J. Adv. Model. Earth Syst.,11, 998-1038, doi:10.1029/2018MS001400,\nMueller, W.A. et al. (2018): A highāresolution version of the Max Planck Institute Earth System Model MPIāESM1.2āHR. J. Adv. Model. EarthSyst.,10,1383ā1413, doi:10.1029/2017MS001217', 'source': 'MPI-ESM1.2-LR (2017): \naerosol: none, prescribed MACv2-SP\natmos: ECHAM6.3 (spectral T63; 192 x 96 longitude/latitude; 47 levels; top level 0.01 hPa)\natmosChem: none\nland: JSBACH3.20\nlandIce: none/prescribed\nocean: MPIOM1.63 (bipolar GR1.5, approximately 1.5deg; 256 x 220 longitude/latitude; 40 levels; top grid cell 0-12 m)\nocnBgchem: HAMOCC6\nseaIce: unnamed (thermodynamic (Semtner zero-layer) dynamic (Hibler 79) sea ice model)', 'source_id': 'MPI-ESM1-2-LR', 'source_type': 'AOGCM', 'sub_experiment': 'none', 'sub_experiment_id': 'none', 'table_id': 'Amon', 'table_info': 'Creation Date:(09 May 2019) MD5:e6ef8ececc8f338646ebfb3aeed36bfc', 'title': 'MPI-ESM1-2-LR output prepared for CMIP6', 'variable_id': 'ts', 'variant_label': 'r1i1p1f1', 'license': 'CMIP6 model data produced by MPI-M is licensed under a Creative Commons Attribution ShareAlike 4.0 International License (https://creativecommons.org/licenses). Consult https://pcmdi.llnl.gov/CMIP6/TermsOfUse for terms of use governing CMIP6 output, including citation requirements and proper acknowledgment. Further information about this data, including some limitations, can be found via the further_info_url (recorded as a global attribute in this file) and. The data producers and data providers make no warranty, either express or implied, including, but not limited to, warranties of merchantability and fitness for a particular purpose. All liabilities arising from the supply of the information (including any liability arising in negligence) are excluded to the fullest extent permitted by law.', 'cmor_version': '3.5.0', 'tracking_id': 'hdl:21.14100/47b33dbd-7fcd-4f09-abcf-25202400ba8c'}
---------------------------
Data variables:
time_bnds (time, bnds) datetime64[ns] ...
lat_bnds (time, lat, bnds) float64 ...
lon_bnds (time, lon, bnds) float64 179.1 -179.1 -179.1 ... 177.2 179.1
ts (time, lat, lon) float32 ...
model.ts[0]
<xarray.DataArray 'ts' (lat: 96, lon: 192)>
[18432 values with dtype=float32]
Coordinates:
time datetime64[ns] 2015-01-16T12:00:00
* lat (lat) float64 88.57 86.72 84.86 83.0 ... -83.0 -84.86 -86.72 -88.57
* lon (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
Attributes:
standard_name: surface_temperature
long_name: Surface Temperature
comment: Temperature of the lower boundary of the atmosphere
units: K
cell_methods: area: time: mean
cell_measures: area: areacella
history: 2019-10-29T16:19:11Z altered by CMOR: replaced missing va...print(model.ts.coords)
Coordinates: * time (time) datetime64[ns] 2015-01-16T12:00:00 ... 2050-12-16T12:00:00 * lat (lat) float64 88.57 86.72 84.86 83.0 ... -83.0 -84.86 -86.72 -88.57 * lon (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
model.ts[0].coords['time']
<xarray.DataArray 'time' ()>
array('2015-01-16T12:00:00.000000000', dtype='datetime64[ns]')
Coordinates:
time datetime64[ns] 2015-01-16T12:00:00
Attributes:
bounds: time_bnds
axis: T
long_name: time
standard_name: timeVisualizzazioni¶
ERA5 2m Temperature (1950-2020)¶
fig = plt.figure(1, figsize=[8,4])
ax = plt.subplot(1, 1, 1, projection=ccrs.PlateCarree())
ax.coastlines()
temp.t2m.mean(dim='time').plot.pcolormesh(ax=ax, cmap='coolwarm')
plt.title("ERA5 Mean Temperature 1950-2023", fontsize=10)
Text(0.5, 1.0, 'ERA5 Mean Temperature 1950-2023')
lat = temp.t2m.coords['latitude'].values
lon = temp.t2m.coords['longitude'].values
t2m_mean = temp.t2m.mean(dim='time')
t2m_mean_df = pd.DataFrame(t2m_mean, lat, lon)
t2m_mean_df
| -180.00 | -179.75 | -179.50 | -179.25 | -179.00 | -178.75 | -178.50 | -178.25 | -178.00 | -177.75 | ... | 177.50 | 177.75 | 178.00 | 178.25 | 178.50 | 178.75 | 179.00 | 179.25 | 179.50 | 179.75 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 90.00 | 258.284973 | 258.284973 | 258.284973 | 258.284973 | 258.284973 | 258.284973 | 258.284973 | 258.284973 | 258.284973 | 258.284973 | ... | 258.284973 | 258.284973 | 258.284973 | 258.284973 | 258.284973 | 258.284973 | 258.284973 | 258.284973 | 258.284973 | 258.284973 |
| 89.75 | 258.236755 | 258.236511 | 258.236237 | 258.235992 | 258.235748 | 258.235474 | 258.235260 | 258.234985 | 258.234955 | 258.234924 | ... | 258.239044 | 258.238892 | 258.238739 | 258.238556 | 258.238251 | 258.238037 | 258.237762 | 258.237488 | 258.237274 | 258.237030 |
| 89.50 | 258.225311 | 258.224823 | 258.224304 | 258.223785 | 258.223297 | 258.222778 | 258.222290 | 258.221741 | 258.221283 | 258.220734 | ... | 258.230316 | 258.229797 | 258.229309 | 258.228790 | 258.228302 | 258.227783 | 258.227325 | 258.226807 | 258.226318 | 258.225800 |
| 89.25 | 258.250488 | 258.249664 | 258.248749 | 258.247650 | 258.246460 | 258.245361 | 258.244232 | 258.243042 | 258.241852 | 258.240753 | ... | 258.255768 | 258.255249 | 258.254791 | 258.254242 | 258.253784 | 258.253296 | 258.252869 | 258.252319 | 258.251831 | 258.251190 |
| 89.00 | 258.236969 | 258.235565 | 258.234222 | 258.232880 | 258.231476 | 258.230133 | 258.228821 | 258.227478 | 258.226105 | 258.224792 | ... | 258.243896 | 258.243195 | 258.242462 | 258.241791 | 258.241089 | 258.240356 | 258.239716 | 258.239014 | 258.238342 | 258.237671 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| -89.00 | 226.847107 | 226.851974 | 226.856812 | 226.861694 | 226.866623 | 226.871506 | 226.876358 | 226.881271 | 226.886169 | 226.891022 | ... | 226.757385 | 226.766342 | 226.775314 | 226.784286 | 226.793259 | 226.802216 | 226.811218 | 226.820190 | 226.829163 | 226.838120 |
| -89.25 | 226.848923 | 226.854141 | 226.859085 | 226.863663 | 226.868164 | 226.872681 | 226.877258 | 226.881775 | 226.886383 | 226.890930 | ... | 226.765427 | 226.774323 | 226.783173 | 226.792130 | 226.801071 | 226.809998 | 226.818878 | 226.827942 | 226.836868 | 226.843643 |
| -89.50 | 226.970963 | 226.974487 | 226.978012 | 226.981552 | 226.985016 | 226.988556 | 226.992065 | 226.995605 | 226.999176 | 227.002716 | ... | 226.936111 | 226.939575 | 226.943024 | 226.946518 | 226.949982 | 226.953476 | 226.956970 | 226.960419 | 226.963928 | 226.967453 |
| -89.75 | 227.077484 | 227.079254 | 227.080948 | 227.082748 | 227.084503 | 227.086273 | 227.088043 | 227.089783 | 227.090134 | 227.090134 | ... | 227.063034 | 227.063660 | 227.064377 | 227.065262 | 227.067001 | 227.068695 | 227.070496 | 227.072235 | 227.074020 | 227.075760 |
| -90.00 | 227.361938 | 227.361938 | 227.361938 | 227.361938 | 227.361938 | 227.361938 | 227.361938 | 227.361938 | 227.361938 | 227.361938 | ... | 227.361938 | 227.361938 | 227.361938 | 227.361938 | 227.361938 | 227.361938 | 227.361938 | 227.361938 | 227.361938 | 227.361938 |
721 rows Ć 1440 columns
fig = plt.figure(figsize=(9,6)) # x,y(inches)
ax = plt.axes(projection=ccrs.Robinson())
ax.set_global()
ax.set_title('Mean 2m Temperature (K)')
mm = ax.pcolormesh(t2m_mean_df.columns, # longitude
t2m_mean_df.index, # latitude
t2m_mean_df, # data
shading='auto', \
transform=ccrs.PlateCarree(),
cmap=mpl.cm.cubehelix )
# more color palettes here:
# https://matplotlib.org/3.1.3/tutorials/colors/colormaps.html
ax.coastlines()
#- add colorbar
cbar_ax = fig.add_axes([0.28, 0.10, 0.46, 0.05]) #[left, bottom, width, height]
cbar = fig.colorbar(mm, cax=cbar_ax, extend='both', orientation='horizontal')
cbar.set_label('Temperature (K)') #($\mu g$ $m^{-3}$)
cbar.ax.tick_params(labelsize=8)
plt.show()
plt.close()
temp.time[-1]
<xarray.DataArray 'time' ()>
array('2022-12-01T00:00:00.000000000', dtype='datetime64[ns]')
Coordinates:
time datetime64[ns] 2022-12-01
Attributes:
long_name: timetemp.sel(time='2022-12-01T00:00:00.000000000')
<xarray.Dataset>
Dimensions: (longitude: 1440, latitude: 721)
Coordinates:
* longitude (longitude) float32 -180.0 -179.8 -179.5 ... 179.5 179.8
* latitude (latitude) float32 90.0 89.75 89.5 ... -89.5 -89.75 -90.0
time datetime64[ns] 2022-12-01
Data variables:
t2m (latitude, longitude) float32 249.8 249.8 ... 244.5 244.5
longitude_bnds (longitude) float32 -180.0 -179.8 -179.5 ... 179.5 179.8
Attributes:
Conventions: CF-1.6
history: 2023-12-06 15:35:43 GMT by grib_to_netcdf-2.25.1: /opt/ecmw...# Combine plot with different projections
fig = plt.figure(1, figsize=[18,9])
# Fix extent
minval = 240
maxval = 310
# Plot 1 for Northern Hemisphere subplot argument (nrows, ncols, nplot)
# here 1 row, 2 columns and 1st plot
ax1 = plt.subplot(1, 2, 1, projection=ccrs.Orthographic(0, 90))
# Plot 2 for Southern Hemisphere
# 2nd plot
ax2 = plt.subplot(1, 2, 2, projection=ccrs.Orthographic(180, -90))
tsel = 0
for ax,t in zip([ax1, ax2], ["Northern", "Southern"]):
map = temp.t2m.sel(time='2022-12-01T00:00:00.000000000').plot(ax=ax, vmin=minval, vmax=maxval,
transform=ccrs.PlateCarree(),
cmap='coolwarm',
add_colorbar=False)
ax.set_title(t + " Hemisphere \n" , fontsize=15)
ax.coastlines()
ax.gridlines()
# Title for both plots
fig.suptitle('ERA5 Air Temperature at 2 meters December 2022', fontsize=20)
cb_ax = fig.add_axes([0.325, 0.05, 0.4, 0.04])
cbar = plt.colorbar(map, cax=cb_ax, extend='both', orientation='horizontal', fraction=0.046, pad=0.04)
cbar.ax.tick_params(labelsize=25)
cbar.ax.set_ylabel('K', fontsize=25)
Text(0, 0.5, 'K')
# Combine plot with different projections
fig = plt.figure(1, figsize=[18,9])
# Fix extent
minval = 220
maxval = 340
# Plot 1 for Northern Hemisphere subplot argument (nrows, ncols, nplot)
# here 1 row, 2 columns and 1st plot
ax1 = plt.subplot(1, 2, 1, projection=ccrs.Orthographic(0, 90))
# Plot 2 for Southern Hemisphere
# 2nd plot
ax2 = plt.subplot(1, 2, 2, projection=ccrs.Orthographic(180, -90))
tsel = 0
for ax,t in zip([ax1, ax2], ["Northern", "Southern"]):
map = t2m_mean.plot(ax=ax, vmin=minval, vmax=maxval,
transform=ccrs.PlateCarree(),
cmap='coolwarm',
add_colorbar=False)
ax.set_title(t + " Hemisphere \n" , fontsize=15)
ax.coastlines()
ax.gridlines()
# Title for both plots
fig.suptitle('ERA5 Mean Air Temperature at 2 meters (K)', fontsize=20)
cb_ax = fig.add_axes([0.325, 0.05, 0.4, 0.04])
cbar = plt.colorbar(map, cax=cb_ax, extend='both', orientation='horizontal', fraction=0.046, pad=0.04)
cbar.ax.tick_params(labelsize=25)
cbar.ax.set_ylabel('K', fontsize=25)
# Plot the mean values of the scenario over longitude and latitude (separating the emispheres)
temp.where(temp.latitude > 0).sel({'time': slice('2000', '2006')}).mean(['latitude', 'longitude']).t2m.plot(label='Northern Hemisphere')
temp.where(temp.latitude < 0).sel({'time': slice('2000', '2006')}).mean(['latitude', 'longitude']).t2m.plot(label='Southern Hemisphere')
plt.xlabel('Year')
plt.ylabel('Temperature (K)')
plt.legend()
plt.title('2m Temperature (K) from 1950 to 2022 per Emisphere')
Text(0.5, 1.0, '2m Temperature (K) from 1950 to 2022 per Emisphere')
La serie storica della temperatura a 2 metri offre uno sguardo approfondito alle dinamiche climatiche dal 1950 al 2022, rivelando variazioni costanti accentuate da una notevole variabilitĆ nell'emisfero settentrionale.
Entrambi gli emisferi presentano chiaramente una marcata variabilità stagionale e annuale, con fasi di aumento termico seguite da periodi di raffreddamento. Tuttavia, emerge chiaramente che l'emisfero settentrionale manifesta una variabilità più accentuata rispetto al suo omologo meridionale. Questa maggiore variabilità nell'emisfero settentrionale potrebbe essere associata a fenomeni atmosferici specifici e a influenze antropogeniche, sottolineando l'importanza di comprendere le sfumature regionali all'interno delle tendenze climatiche globali.
temp.sel({'time': slice('2000', '2006')}).t2m.mean(dim='longitude').plot(cmap='jet')
<matplotlib.collections.QuadMesh at 0x16c97a340>
# Compute the average yearly temperature
# first mean of all year, mean between all years, and mean on lat lon
annual_mean_temperature = temp.t2m.resample(time='Y', label='right').mean(dim='time').mean(dim='time').mean().values
print('The Annual Mean Temperature between 1950 and 2022 is:', annual_mean_temperature)
The Annual Mean Temperature between 1950 and 2022 is: 278.19336
plt.figure(1, figsize=[8,5])
temp.t2m.sel({'time': slice('2000', '2006')}).isel(longitude=10, latitude=[50, 70, 100]).plot.line(x="time")
[<matplotlib.lines.Line2D at 0x16c8c5d90>, <matplotlib.lines.Line2D at 0x29777c100>, <matplotlib.lines.Line2D at 0x2977ae250>]
seasonal_mean = temp.t2m.groupby("time.season").mean()
seasonal_mean
<xarray.DataArray 't2m' (season: 4, latitude: 721, longitude: 1440)>
array([[[246.63531, 246.63531, 246.63531, ..., 246.63531, 246.63531,
246.63531],
[246.54356, 246.54324, 246.5429 , ..., 246.54463, 246.5443 ,
246.54396],
[246.49258, 246.49194, 246.4911 , ..., 246.49492, 246.49413,
246.49336],
...,
[241.01112, 241.01501, 241.01889, ..., 240.99956, 241.00345,
241.00728],
[241.32678, 241.32893, 241.33073, ..., 241.32109, 241.32304,
241.32489],
[241.55281, 241.55281, 241.55281, ..., 241.55281, 241.55281,
241.55281]],
[[273.38235, 273.38235, 273.38235, ..., 273.38235, 273.38235,
273.38235],
[273.37372, 273.3737 , 273.37366, ..., 273.37347, 273.37357,
273.3736 ],
[273.36996, 273.37006, 273.37018, ..., 273.36972, 273.3699 ,
273.3699 ],
...
[221.97836, 221.9818 , 221.9852 , ..., 221.96805, 221.97148,
221.97499],
[221.92917, 221.93082, 221.9325 , ..., 221.92412, 221.9259 ,
221.9275 ],
[222.18954, 222.18954, 222.18954, ..., 222.18954, 222.18954,
222.18954]],
[[258.26337, 258.26337, 258.26337, ..., 258.26337, 258.26337,
258.26337],
[258.22498, 258.22458, 258.22415, ..., 258.22632, 258.22595,
258.2255 ],
[258.23306, 258.23206, 258.2312 , ..., 258.23547, 258.23465,
258.2339 ],
...,
[226.52615, 226.52963, 226.53313, ..., 226.51569, 226.5191 ,
226.52267],
[226.66017, 226.66197, 226.66364, ..., 226.65492, 226.65665,
226.65843],
[226.95596, 226.95596, 226.95596, ..., 226.95596, 226.95596,
226.95596]]], dtype=float32)
Coordinates:
* longitude (longitude) float32 -180.0 -179.8 -179.5 ... 179.2 179.5 179.8
* latitude (latitude) float32 90.0 89.75 89.5 89.25 ... -89.5 -89.75 -90.0
* season (season) object 'DJF' 'JJA' 'MAM' 'SON'
Attributes:
units: K
long_name: 2 metre temperatureseasonal_mean.plot(col="season",
cmap = 'coolwarm',
aspect=1.3,
size=5, col_wrap=2)
<xarray.plot.facetgrid.FacetGrid at 0x1667e3d30>
seasonal_mean.plot.contour(col="season", cmap = 'coolwarm',
levels=20, add_colorbar=True,
aspect=1.3, size=5, col_wrap=2)
<xarray.plot.facetgrid.FacetGrid at 0x166902430>
MPI-ESM1-2-LR (2015-2050)¶
# Plot the mean values of scenario over time
fig = plt.figure(1, figsize=[8,4])
ax = plt.subplot(1, 1, 1, projection=ccrs.PlateCarree())
ax.coastlines()
model.ts.mean(dim='time').plot.pcolormesh(ax=ax, cmap='coolwarm')
plt.title("Mean Surface Temperature MPI-ESM1-2-LR model 2015-2050", fontsize=11)
Text(0.5, 1.0, 'Mean Surface Temperature MPI-ESM1-2-LR model 2015-2050')
Dal plot si nota subito che il modello ha una risoluzione spaziale della temperatura minore rispetto ai dati di ERA5.
lat = model.ts.coords['lat'].values
lon = model.ts.coords['lon'].values
model_mean = model.ts.mean(dim='time')
ts_mean = pd.DataFrame(model_mean, lat, lon)
ts_mean
| -180.000 | -178.125 | -176.250 | -174.375 | -172.500 | -170.625 | -168.750 | -166.875 | -165.000 | -163.125 | ... | 161.250 | 163.125 | 165.000 | 166.875 | 168.750 | 170.625 | 172.500 | 174.375 | 176.250 | 178.125 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 88.572169 | 260.103149 | 260.099792 | 260.099487 | 260.097870 | 260.094086 | 260.093353 | 260.091583 | 260.091949 | 260.092010 | 260.089417 | ... | 260.097229 | 260.097260 | 260.095276 | 260.113922 | 260.108826 | 260.110382 | 260.109985 | 260.108124 | 260.107147 | 260.103516 |
| 86.722531 | 259.741211 | 259.730194 | 259.713104 | 259.699188 | 259.690857 | 259.679016 | 259.664429 | 259.646210 | 259.633331 | 259.620728 | ... | 259.908203 | 259.890289 | 259.877747 | 259.858429 | 259.843109 | 259.823120 | 259.806763 | 259.789093 | 259.769470 | 259.759064 |
| 84.861970 | 259.824249 | 259.796143 | 259.773468 | 259.753876 | 259.732086 | 259.705200 | 259.674835 | 259.647095 | 259.621796 | 259.597931 | ... | 259.992432 | 259.977112 | 259.961060 | 259.938263 | 259.928192 | 259.912476 | 259.893311 | 259.876160 | 259.858551 | 259.844818 |
| 82.998942 | 259.946472 | 259.917633 | 259.896301 | 259.873474 | 259.847321 | 259.822021 | 259.790009 | 259.749023 | 259.719299 | 259.694672 | ... | 260.088989 | 260.073181 | 260.061249 | 260.039093 | 260.032623 | 260.020172 | 260.006195 | 259.995148 | 259.983154 | 259.964142 |
| 81.134977 | 260.090454 | 260.078003 | 260.058838 | 260.040894 | 260.016876 | 259.989685 | 259.959442 | 259.925446 | 259.890015 | 259.853088 | ... | 260.189880 | 260.170654 | 260.161133 | 260.154022 | 260.151031 | 260.141541 | 260.128113 | 260.125061 | 260.115448 | 260.110138 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| -81.134977 | 248.326477 | 248.194077 | 248.154984 | 248.168869 | 248.186539 | 248.198257 | 248.215897 | 248.234070 | 248.263855 | 248.307037 | ... | 244.064560 | 245.637085 | 246.972427 | 248.030731 | 248.797241 | 249.255966 | 249.400223 | 249.267288 | 248.957977 | 248.600525 |
| -82.998942 | 250.572098 | 250.398438 | 250.187897 | 250.015076 | 249.828400 | 249.626816 | 249.434219 | 249.227509 | 249.014145 | 248.828537 | ... | 245.761887 | 247.000717 | 248.087006 | 249.007034 | 249.731476 | 250.267761 | 250.602463 | 250.784958 | 250.806381 | 250.726089 |
| -84.861970 | 248.406952 | 248.755264 | 249.018417 | 249.211288 | 249.361786 | 249.457336 | 249.507828 | 249.527969 | 249.522736 | 249.483780 | ... | 240.669113 | 241.760925 | 242.806900 | 243.785812 | 244.690903 | 245.511551 | 246.255020 | 246.913254 | 247.485382 | 247.987900 |
| -86.722531 | 236.855469 | 237.310089 | 237.715744 | 238.100510 | 238.449768 | 238.743454 | 239.019623 | 239.261536 | 239.457214 | 239.623947 | ... | 230.999786 | 231.671585 | 232.343323 | 232.993622 | 233.605728 | 234.205673 | 234.777603 | 235.338211 | 235.870743 | 236.379333 |
| -88.572169 | 225.119522 | 225.067841 | 225.011093 | 224.959503 | 224.909012 | 224.857635 | 224.800842 | 224.753418 | 224.701660 | 224.658264 | ... | 225.627762 | 225.593842 | 225.540955 | 225.497452 | 225.453629 | 225.401276 | 225.353943 | 225.287537 | 225.236465 | 225.178360 |
96 rows Ć 192 columns
fig = plt.figure(figsize=(9,6)) # x,y(inches)
ax = plt.axes(projection=ccrs.Robinson())
ax.set_global()
ax.set_title('Mean Temperature (hPa)')
mm = ax.pcolormesh(ts_mean.columns, # longitude
ts_mean.index, # latitude
ts_mean, # data
shading='auto', \
transform=ccrs.PlateCarree(),
cmap=mpl.cm.cubehelix )
ax.coastlines()
#- add colorbar
cbar_ax = fig.add_axes([0.28, 0.10, 0.46, 0.05]) #[left, bottom, width, height]
cbar = fig.colorbar(mm, cax=cbar_ax, extend='both', orientation='horizontal')
cbar.set_label('Temperature (K)') #($\mu g$ $m^{-3}$)
cbar.ax.tick_params(labelsize=8)
plt.show()
plt.close()
model
<xarray.Dataset>
Dimensions: (time: 432, bnds: 2, lat: 96, lon: 192)
Coordinates:
* time (time) datetime64[ns] 2015-01-16T12:00:00 ... 2050-12-16T12:00:00
* lat (lat) float64 88.57 86.72 84.86 83.0 ... -84.86 -86.72 -88.57
* lon (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
Dimensions without coordinates: bnds
Data variables:
time_bnds (time, bnds) datetime64[ns] ...
lat_bnds (time, lat, bnds) float64 ...
lon_bnds (time, lon, bnds) float64 179.1 -179.1 -179.1 ... 177.2 179.1
ts (time, lat, lon) float32 251.7 251.6 251.6 ... 239.4 239.5 239.4
Attributes: (12/47)
Conventions: CF-1.7 CMIP-6.2
activity_id: ScenarioMIP
branch_method: standard
branch_time_in_child: 60265.0
branch_time_in_parent: 60265.0
contact: cmip6-mpi-esm@dkrz.de
... ...
title: MPI-ESM1-2-LR output prepared for CMIP6
variable_id: ts
variant_label: r1i1p1f1
license: CMIP6 model data produced by MPI-M is licensed un...
cmor_version: 3.5.0
tracking_id: hdl:21.14100/47b33dbd-7fcd-4f09-abcf-25202400ba8cmodel.time[2].values
numpy.datetime64('2015-03-16T12:00:00.000000000')
# Combine plot with different projections
fig = plt.figure(1, figsize=[18,9])
# Fix extent
minval = 240
maxval = 310
# Plot 1 for Northern Hemisphere subplot argument (nrows, ncols, nplot)
# here 1 row, 2 columns and 1st plot
ax1 = plt.subplot(1, 2, 1, projection=ccrs.Orthographic(0, 90))
# Plot 2 for Southern Hemisphere
# 2nd plot
ax2 = plt.subplot(1, 2, 2, projection=ccrs.Orthographic(180, -90))
tsel = 0
for ax,t in zip([ax1, ax2], ["Northern", "Southern"]):
map = model.ts.sel(time='2022-12-16T12:00:00.000000000').plot(ax=ax, vmin=minval, vmax=maxval,
transform=ccrs.PlateCarree(),
cmap='coolwarm',
add_colorbar=False)
ax.set_title(t + " Hemisphere \n" , fontsize=15)
ax.coastlines()
ax.gridlines()
# Title for both plots
fig.suptitle('MPI-ESM1-2-LR Surface Temperature December 2022', fontsize=20)
cb_ax = fig.add_axes([0.325, 0.05, 0.4, 0.04])
cbar = plt.colorbar(map, cax=cb_ax, extend='both', orientation='horizontal', fraction=0.046, pad=0.04)
cbar.ax.tick_params(labelsize=25)
cbar.ax.set_ylabel('K', fontsize=25)
Text(0, 0.5, 'K')
# Plot the mean values of the scenario over longitude and latitude
model.mean(['lat', 'lon']).ts.sel({'time': slice('2020', '2030')}).plot()
plt.xlabel('Year')
plt.ylabel('Temperature (K)')
plt.title('Mean Temperature (K) from 2020 to 2030')
Text(0.5, 1.0, 'Mean Temperature (K) from 2020 to 2030')
# Plot the mean values of the scenario over longitude and latitude (separating the emispheres)
model.where(model.lat > 0).sel({'time': slice('2020', '2030')}).mean(['lat', 'lon']).ts.plot(label='Northern Hemisphere')
model.where(model.lat < 0).sel({'time': slice('2020', '2030')}).mean(['lat', 'lon']).ts.plot(label='Southern Hemisphere')
plt.xlabel('Year')
plt.ylabel('Temperature (K)')
plt.legend()
plt.title('Mean Temperature (K) from 2020 to 2030 per Emisphere')
Text(0.5, 1.0, 'Mean Temperature (K) from 2020 to 2030 per Emisphere')
# Plot the mean values of the scenario over longitude
model.sel({'time': slice('2025', '2030')}).ts.mean(dim='lon').plot(cmap='jet')
plt.title('Mean Temperature (K) from 2025 to 2030 over Longitude')
Text(0.5, 1.0, 'Mean Temperature (K) from 2025 to 2030 over Longitude')
# Compute the average yearly temperature
# first mean of all year, mean between all years, and mean on lat lon
annual_mean_temperature = model.ts.resample(time='Y', label='right').mean(dim='time').mean(dim='time').mean().values
print('The Annual Mean Temperature between 2015 and 2050 is:', annual_mean_temperature)
The Annual Mean Temperature between 2015 and 2050 is: 279.9384
seasonal_mean = model.ts.groupby("time.season").mean()
seasonal_mean
<xarray.DataArray 'ts' (season: 4, lat: 96, lon: 192)>
array([[[248.87894, 248.87198, 248.86989, ..., 248.89072, 248.88844,
248.88123],
[248.07092, 248.04707, 248.01292, ..., 248.15842, 248.11932,
248.0947 ],
[247.90872, 247.85469, 247.82776, ..., 248.01189, 247.96515,
247.94545],
...,
[257.3325 , 257.80054, 258.19406, ..., 255.68124, 256.2763 ,
256.82108],
[247.97238, 248.33287, 248.65443, ..., 246.82402, 247.2071 ,
247.61543],
[240.62566, 240.55457, 240.48662, ..., 240.8471 , 240.7822 ,
240.70201]],
[[272.3684 , 272.36765, 272.36838, ..., 272.3676 , 272.36774,
272.368 ],
[272.2623 , 272.2638 , 272.26178, ..., 272.26453, 272.26425,
272.2638 ],
[272.28043, 272.2813 , 272.27884, ..., 272.281 , 272.28177,
272.2816 ],
...
[247.24951, 247.65834, 247.96422, ..., 245.59352, 246.21046,
246.78401],
[234.71307, 235.23962, 235.72263, ..., 232.98836, 233.601 ,
234.16745],
[221.24445, 221.1628 , 221.07278, ..., 221.5279 , 221.4447 ,
221.34277]],
[[262.89508, 262.89426, 262.89685, ..., 262.89224, 262.89383,
262.89386],
[262.50146, 262.49057, 262.47885, ..., 262.53265, 262.51883,
262.52063],
[262.74728, 262.71243, 262.66528, ..., 262.8038 , 262.7867 ,
262.77185],
...,
[246.47685, 246.75137, 246.95355, ..., 245.09572, 245.64685,
246.11057],
[235.14192, 235.58359, 235.98395, ..., 233.61662, 234.15804,
234.65663],
[223.24113, 223.19824, 223.15482, ..., 223.35623, 223.3241 ,
223.28383]]], dtype=float32)
Coordinates:
* lat (lat) float64 88.57 86.72 84.86 83.0 ... -83.0 -84.86 -86.72 -88.57
* lon (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
* season (season) object 'DJF' 'JJA' 'MAM' 'SON'
Attributes:
standard_name: surface_temperature
long_name: Surface Temperature
comment: Temperature of the lower boundary of the atmosphere
units: K
cell_methods: area: time: mean
cell_measures: area: areacella
history: 2019-10-29T16:19:11Z altered by CMOR: replaced missing va...seasonal_mean.plot(col="season",
cmap = 'coolwarm',
aspect=1.3,
size=5, col_wrap=2)
<xarray.plot.facetgrid.FacetGrid at 0x166577790>
seasonal_mean.plot.contour(col="season", cmap = 'coolwarm',
levels=20, add_colorbar=True,
aspect=1.3, size=5, col_wrap=2)
<xarray.plot.facetgrid.FacetGrid at 0x15855e130>
Aggregazione Spaziale: Regridding¶
Confrontando le dimensioni delle griglie spaziali dei due dataset si osserva che:
- ERA5 dataset shape: 721 (latitudine) x 1440 (longitudine)
- MPI-ESM1-2-LR dataset shape: 96 (latitudine) x 192 (longitudine)
Le differenze significative nelle risoluzioni spaziali richiedono una corretta procedura di regridding. à importante notare che l'ERA5 ha una risoluzione spaziale più elevata rispetto al MPI-ESM1-2-LR. Il regridding dovrebbe essere eseguito in modo attento per preservare al meglio le caratteristiche spaziali originali durante l'allineamento delle griglie. Questo assicurerà che i due set di dati siano comparabili e utilizzabili in modo significativo per le analisi climatiche.
print('ERA5 dataset shape:\t\t', temp.dims['latitude'], temp.dims['longitude'])
print('MPI-ESM1-2-LR dataset shape:\t', model.dims['lat'], model.dims['lon'])
ERA5 dataset shape: 721 1440 MPI-ESM1-2-LR dataset shape: 96 192
# Plot grid comparison over Italy
ax = plt.axes(projection=ccrs.PlateCarree())
ax.set_extent([5, 20, 35, 50]) # select italy
ax.coastlines()
ax.add_feature(cf.BORDERS)
gl1 = ax.gridlines(color='tab:orange') #
gl1.xlocator = ticker.FixedLocator(temp.longitude.values)
gl1.ylocator = ticker.FixedLocator(temp.latitude.values)
gl2 = ax.gridlines(color='tab:blue') #
gl2.xlocator = ticker.FixedLocator(model.lon.values)
gl2.ylocator = ticker.FixedLocator(model.lat.values)
La risoluzione di ERA5 è estremamente maggiore rispetto a MPI-ESM1-2-LR, questo si nota notevolmente da questo output. La risoluzione più bassa che si ottiene dal regridding rende il dataset non ottimale per predizioni metereologiche a livello locale e a breve termine.
temp_lin = temp.interp(latitude=model.lat, longitude=model.lon, method='linear')
temp_near = temp.interp(latitude=model.lat, longitude=model.lon, method='nearest')
Utilizzo due metodi di regridding: "nearest" e "linear. come metodo di regridding quando la semplicità e la velocità sono prioritarie, mentre "linear" è scelto per preservare meglio le tendenze continue e le variazioni spaziali nei dati durante l'allineamento delle griglie spaziali tra ERA5 (risoluzione più alta) e MPI-ESM1-2-LR (risoluzione più bassa).
# Check which one preserves the global mean temperature
print('Original average temperature:\t', temp.t2m.mean(['time', 'latitude', 'longitude']).values)
print('Linear average temperature:\t', temp_lin.t2m.mean(['time', 'lat', 'lon']).values)
print('Nearest average temperature.:\t', temp_near.t2m.mean(['time', 'lat', 'lon']).values)
Original average temperature: 278.19754 Linear average temperature: 278.4317843603806 Nearest average temperature.: 278.4387
I risultati indicano che, dopo il regridding, l'average temperature ottenuto utilizzando il metodo "linear" ĆØ 278.43, mentre con il metodo "nearest" ĆØ 278.44. BenchĆ© simili, scelgo il metodo "nearest". Questo approccio ĆØ spesso preferito per la sua semplicitĆ e velocitĆ
Prima di applicare ogni operazione nel tempo, ĆØ essenziale assicurarsi che le coordinate temporali di entrambi i dataset coincidano, consentendo confronti significativi.
# Identify the time range of interest in ds_era5
start_date_temp = temp_near['time'].min().values
end_date_temp = temp_near['time'].max().values
start_date_temp, end_date_temp
(numpy.datetime64('1950-01-01T00:00:00.000000000'),
numpy.datetime64('2022-12-01T00:00:00.000000000'))
# Identify the time range of interest in ds_era5
start_date_model = model['time'].min().values
end_date_model = model['time'].max().values
start_date_model, end_date_model
(numpy.datetime64('2015-01-16T12:00:00.000000000'),
numpy.datetime64('2050-12-16T12:00:00.000000000'))
L'intervallo temporale comune tra il modello e le osservazioni va dal 2015-01-16T12:00:00 al 2022-12-01T00:00:00.
temp_near_aligned = temp_near.sel(time=slice(start_date_model, end_date_temp))
model_aligned = model.sel(time=slice(start_date_model, end_date_temp))
temp_near_aligned
<xarray.Dataset>
Dimensions: (time: 95, lat: 96, lon: 192)
Coordinates:
* time (time) datetime64[ns] 2015-02-01 2015-03-01 ... 2022-12-01
latitude (lat) float64 88.57 86.72 84.86 83.0 ... -84.86 -86.72 -88.57
longitude (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
* lat (lat) float64 88.57 86.72 84.86 83.0 ... -84.86 -86.72 -88.57
* lon (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
month (time) int64 2 3 4 5 6 7 8 9 10 11 12 ... 3 4 5 6 7 8 9 10 11 12
Data variables:
t2m (time, lat, lon) float32 nan nan nan nan ... 243.8 243.8 243.8
anomalies (time, lat, lon) float32 nan nan nan nan ... 0.9939 0.9665 0.9411
Attributes:
Conventions: CF-1.6
history: 2023-12-06 15:35:43 GMT by grib_to_netcdf-2.25.1: /opt/ecmw...model_aligned
<xarray.Dataset>
Dimensions: (time: 95, bnds: 2, lat: 96, lon: 192)
Coordinates:
* time (time) datetime64[ns] 2015-01-16T12:00:00 ... 2022-11-16
* lat (lat) float64 88.57 86.72 84.86 83.0 ... -84.86 -86.72 -88.57
* lon (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
latitude (lat) float64 88.57 86.72 84.86 83.0 ... -84.86 -86.72 -88.57
longitude (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
month (time) int64 1 2 3 4 5 6 7 8 9 10 11 ... 1 2 3 4 5 6 7 8 9 10 11
Dimensions without coordinates: bnds
Data variables:
time_bnds (time, bnds) datetime64[ns] ...
lat_bnds (time, lat, bnds) float64 ...
lon_bnds (time, lon, bnds) float64 179.1 -179.1 -179.1 ... 177.2 179.1
ts (time, lat, lon) float32 ...
anomalies (time, lat, lon) float32 nan nan nan nan ... -3.034 -3.093 -3.144
Attributes: (12/47)
Conventions: CF-1.7 CMIP-6.2
activity_id: ScenarioMIP
branch_method: standard
branch_time_in_child: 60265.0
branch_time_in_parent: 60265.0
contact: cmip6-mpi-esm@dkrz.de
... ...
title: MPI-ESM1-2-LR output prepared for CMIP6
variable_id: ts
variant_label: r1i1p1f1
license: CMIP6 model data produced by MPI-M is licensed un...
cmor_version: 3.5.0
tracking_id: hdl:21.14100/47b33dbd-7fcd-4f09-abcf-25202400ba8cOra, entrambi i dataset presentano coordinate temporali allineate.
fig, axs = plt.subplots(1, 2, figsize=[12, 4], subplot_kw={'projection': ccrs.PlateCarree()})
axs[0].coastlines()
temp_near_aligned.t2m.mean(dim='time').plot.pcolormesh(ax=axs[0], cmap='coolwarm')
axs[0].set_title("ERA5 Mean Temperature 2015-2023", fontsize=10)
axs[1].coastlines()
model_aligned.ts.mean(dim='time').plot.pcolormesh(ax=axs[1], cmap='coolwarm')
axs[1].set_title("MPI-ESM1-2-LR Mean Temperature 2015-2023", fontsize=10)
plt.tight_layout()
plt.show()
Nel confronto tra i dati reali di ERA5 e le simulazioni del modello MPI-ESM1-2-LR, noto che la media delle temperature effettive risulta essere più elevata rispetto alle previsioni del modello nello stesso periodo (2015-2023). Analizzare attentamente queste differenze è fondamentale per comprendere le possibili variazioni nei parametri del modello o altre influenze che possono contribuire a queste disparità e valutare la robustezza delle simulazioni climatiche.
temp_near_aligned.mean(['lat', 'lon']).t2m.plot(label='ERA5 Surface Temperature')
model_aligned.mean(['lat', 'lon']).ts.plot(label='MPI-ESM1-2-LR Surface Temperature')
plt.xlabel('Year')
plt.ylabel('Temperature (K)')
plt.legend()
plt.title('Surface Temperature (K) from 2015 to 2022 Observations vs Simulation')
Text(0.5, 1.0, 'Surface Temperature (K) from 2015 to 2022 Observations vs Simulation')
Test se le Differenze sono Statisticamente Significative¶
Per verificare se la differenza ĆØ statisticamente significativa, utilizzo un test statistico come il t-test di Student.
# Calculate the mean over latitude and longitude
mean_obs = temp_near_aligned.mean(['lat', 'lon']).t2m
mean_model = model_aligned.mean(['lat', 'lon']).ts
# Perform a two-sample t-test
t_stat, p_value = stats.ttest_rel(mean_obs, mean_model)
# Check the p-value to determine significance
alpha = 0.05
if p_value < alpha:
print("The difference is statistically significant (p-value: {:.4f})".format(p_value))
else:
print("The difference is not statistically significant (p-value: {:.4f})".format(p_value))
The difference is statistically significant (p-value: 0.0000)
Il test statistico è stato condotto per confrontare le temperature medie derivate dalle osservazioni di ERA5 e dalla simulazione del modello MPI-ESM1-2-LR. Il termine "differenza" si riferisce al contrasto tra questi due set di dati. Il test statistico tiene conto della variabilità delle temperature nello spazio. Un valore p di 0,0000 indica una forte significatività statistica, supportando l'idea che la differenza osservata tra le temperature della superficie nelle osservazioni e nelle simulazioni non è probabilmente dovuta al caso. Vi è una robusta evidenza statistica di divergenza tra le temperature osservate e quelle simulate. Nel contesto della temperatura, ciò potrebbe significare che ci sono aree o periodi in cui le temperature simulate si discostano significativamente dalle temperature osservate.
Sebbene la significativitĆ statistica sia importante, ĆØ altrettanto utile valutare la significativitĆ pratica delle differenze. Le variazioni sono abbastanza ampie da avere implicazioni significative per gli studi climatici o le applicazioni?
L'analisi delle anomalie nei dati geospaziali comporta la valutazione delle deviazioni rispetto a una linea di base o climatologia. Nel contesto dei dati di temperatura, le anomalie rappresentano la deviazione dei singoli valori di temperatura dalla media a lungo termine o dalle normali.
Le normali climatiche fungono da punto di riferimento con cui ĆØ possibile confrontare le osservazioni recenti o attuali, fornendo una base per molti dataset climatici basati sul concetto di anomalie.
Il periodo dal 1961 al 1990 ĆØ stato mantenuto come periodo di riferimento standard per valutazioni a lungo termine dei cambiamenti climatici (Linee guida dell'OMM sul Calcolo delle Normali Climatiche - edizione 2017).
# Define reference period
yref0 = 1961
yref1 = 1990
# Compute normals on ERA5 dataset
normals = temp_near.sel({'time': slice(str(yref0), str(yref1))}).groupby('time.month').mean()
normals
<xarray.Dataset>
Dimensions: (month: 12, lat: 96, lon: 192)
Coordinates:
latitude (lat) float64 88.57 86.72 84.86 83.0 ... -84.86 -86.72 -88.57
longitude (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
* lat (lat) float64 88.57 86.72 84.86 83.0 ... -84.86 -86.72 -88.57
* lon (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
* month (month) int64 1 2 3 4 5 6 7 8 9 10 11 12
Data variables:
t2m (month, lat, lon) float32 nan nan nan nan ... 242.9 242.9 242.9
Attributes:
Conventions: CF-1.6
history: 2023-12-06 15:35:43 GMT by grib_to_netcdf-2.25.1: /opt/ecmw...# Plot the normals for every month
month = 1
f, ax = plt.subplots(4, 3, figsize=(10, 10))
for i in ax:
for j in i:
normals.sel({'month': month}).t2m.plot(ax=j, add_colorbar=False, cmap='coolwarm')
j.set_title(f"Month: {month}")
month += 1
plt.tight_layout()
# Compute ERA5 and Model anomalies
temp_near['anomalies'] = (temp_near.groupby('time.month') - normals).t2m
model['anomalies'] = (model.ts.groupby('time.month') -normals).t2m
temp_near
<xarray.Dataset>
Dimensions: (time: 876, lat: 96, lon: 192)
Coordinates:
* time (time) datetime64[ns] 1950-01-01 1950-02-01 ... 2022-12-01
latitude (lat) float64 88.57 86.72 84.86 ... -84.86 -86.72 -88.57
longitude (lon) float64 -180.0 -178.1 -176.2 ... 174.4 176.2 178.1
* lat (lat) float64 88.57 86.72 84.86 ... -84.86 -86.72 -88.57
* lon (lon) float64 -180.0 -178.1 -176.2 ... 174.4 176.2 178.1
month (time) int64 1 2 3 4 5 6 7 8 9 10 ... 3 4 5 6 7 8 9 10 11 12
Data variables:
t2m (time, lat, lon) float32 241.7 241.7 241.6 ... 243.8 243.8
longitude_bnds (lon) float32 -180.0 -178.2 -176.2 ... 174.2 176.2 178.0
anomalies (time, lat, lon) float32 -3.581 -3.606 ... 0.9665 0.9411
Attributes:
Conventions: CF-1.6
history: 2023-12-06 15:35:43 GMT by grib_to_netcdf-2.25.1: /opt/ecmw...model
<xarray.Dataset>
Dimensions: (time: 432, bnds: 2, lat: 96, lon: 192)
Coordinates:
* time (time) datetime64[ns] 2015-01-16T12:00:00 ... 2050-12-16T12:00:00
* lat (lat) float64 88.57 86.72 84.86 83.0 ... -84.86 -86.72 -88.57
* lon (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
latitude (lat) float64 88.57 86.72 84.86 83.0 ... -84.86 -86.72 -88.57
longitude (lon) float64 -180.0 -178.1 -176.2 -174.4 ... 174.4 176.2 178.1
month (time) int64 1 2 3 4 5 6 7 8 9 10 11 ... 2 3 4 5 6 7 8 9 10 11 12
Dimensions without coordinates: bnds
Data variables:
time_bnds (time, bnds) datetime64[ns] ...
lat_bnds (time, lat, bnds) float64 ...
lon_bnds (time, lon, bnds) float64 179.1 -179.1 -179.1 ... 177.2 179.1
ts (time, lat, lon) float32 ...
anomalies (time, lat, lon) float32 6.39 6.343 6.362 ... -3.474 -3.41 -3.47
Attributes: (12/47)
Conventions: CF-1.7 CMIP-6.2
activity_id: ScenarioMIP
branch_method: standard
branch_time_in_child: 60265.0
branch_time_in_parent: 60265.0
contact: cmip6-mpi-esm@dkrz.de
... ...
title: MPI-ESM1-2-LR output prepared for CMIP6
variable_id: ts
variant_label: r1i1p1f1
license: CMIP6 model data produced by MPI-M is licensed un...
cmor_version: 3.5.0
tracking_id: hdl:21.14100/47b33dbd-7fcd-4f09-abcf-25202400ba8c# Plot ERA5 anomalies
temp_near['anomalies'].mean('time').plot()
<matplotlib.collections.QuadMesh at 0x297a4a130>
# Plotting
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(6.5, 6))
# Original Temperature
temp_near.t2m.mean(dim='time').plot(ax=ax1, cmap='coolwarm')
ax1.set_title('Mean Temperature (1950-2022)')
ax1.set_ylabel('Temperature (K)')
# Temperature Anomalies
temp_near.anomalies.mean(dim='time').plot(ax=ax2, cmap='coolwarm')
ax2.set_title('Temperature Anomalies (Deviation from Normals)')
ax2.set_ylabel('Temperature Anomaly (K)')
plt.tight_layout()
plt.show()
# Plot the temperature anomalies
temp_near.mean(['lat', 'lon']).anomalies.plot()
plt.xlabel('')
plt.ylabel('Temperature anomalies observations (K)')
Text(0, 0.5, 'Temperature anomalies observations (K)')
model['anomalies'].mean('time').plot()
<matplotlib.collections.QuadMesh at 0x299e4b610>
# Plotting
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(6.5, 6))
# Original Temperature
model.ts.mean(dim='time').plot(ax=ax1, cmap='coolwarm')
ax1.set_title('Mean Temperature (2015-2050)')
ax1.set_ylabel('Temperature (K)')
# Temperature Anomalies
model.anomalies.mean(dim='time').plot(ax=ax2, cmap='coolwarm')
ax2.set_title('Temperature Anomalies (Deviation from Normals)')
ax2.set_ylabel('Temperature Anomaly (K)')
plt.tight_layout()
plt.show()
# Plot the temperature anomalies
model.mean(['lat', 'lon']).anomalies.plot()
plt.xlabel('')
plt.ylabel('Temperature anomalies simulation (K)')
Text(0, 0.5, 'Temperature anomalies simulation (K)')
fig, axs = plt.subplots(1, 2, figsize=[12, 4], subplot_kw={'projection': ccrs.PlateCarree()})
axs[0].coastlines()
temp_near_aligned.t2m.mean(dim='time').plot.pcolormesh(ax=axs[0], cmap='coolwarm')
axs[0].set_title("ERA5 Mean Temperature 2015-2023", fontsize=10)
axs[1].coastlines()
model_aligned.ts.mean(dim='time').plot.pcolormesh(ax=axs[1], cmap='coolwarm')
axs[1].set_title("MPI-ESM1-2-LR Mean Temperature 2015-2023", fontsize=10)
plt.tight_layout()
plt.show()
temp_near_aligned = temp_near.sel(time=slice(start_date_model, end_date_temp))
model_aligned = model.sel(time=slice(start_date_model, end_date_temp))
import matplotlib.pyplot as plt
# Assuming you have two different models: temp_near_aligned and model_aligned
# Plotting
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(13, 8))
# Original Temperature for temp_near_aligned
temp_near_aligned.t2m.mean(dim='time').plot(ax=ax1, cmap='coolwarm', label='temp_near_aligned')
ax1.set_title('Mean Temperature (ERA5 Observations)')
ax1.set_ylabel('Temperature (K)')
ax1.legend()
# Temperature Anomalies for temp_near_aligned
temp_near_aligned.anomalies.mean(dim='time').plot(ax=ax3, cmap='coolwarm', label='temp_near_aligned')
ax3.set_title('Temperature Anomalies from Normals (ERA5 Observations)')
ax3.set_ylabel('Temperature Anomaly (K)')
ax3.legend()
# Original Temperature for model_aligned
model_aligned.ts.mean(dim='time').plot(ax=ax2, cmap='coolwarm', label='model_aligned')
ax2.set_title('Mean Temperature (MPI-ESM1-2-LR Simulations)')
ax2.set_ylabel('Temperature (K)')
ax2.legend()
# Temperature Anomalies for model_aligned
model_aligned.anomalies.mean(dim='time').plot(ax=ax4, cmap='coolwarm', label='model_aligned')
ax4.set_title('Temperature Anomalies from Normals (MPI-ESM1-2-LR Simulations)')
ax4.set_ylabel('Temperature Anomaly (K)')
ax4.legend()
plt.tight_layout()
plt.show()
No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument. No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument. No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument. No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
temp_near_aligned.mean(['lat', 'lon']).anomalies.plot(label='ERA5 Surface Temperature Anomalies')
model_aligned.mean(['lat', 'lon']).anomalies.plot(label='MPI-ESM1-2-LR Surface Temperature Anomalies')
plt.xlabel('Year')
plt.ylabel('Temperature (K)')
plt.legend()
plt.title('Surface Temperature (K) Anomalies from 2015 to 2022 Observations vs Simulation')
Text(0.5, 1.0, 'Surface Temperature (K) Anomalies from 2015 to 2022 Observations vs Simulation')
Test se le Differenze sono Statisticamente Significative¶
Per verificare se la differenza ĆØ statisticamente significativa, utilizzo un test statistico come il t-test di Student.
# Calculate the mean over latitude and longitude
mean_obs = temp_near_aligned.mean(['lat', 'lon']).anomalies
mean_model = model_aligned.mean(['lat', 'lon']).anomalies
# Perform a two-sample t-test
t_stat, p_value = stats.ttest_rel(mean_obs, mean_model)
# Check the p-value to determine significance
alpha = 0.05
if p_value < alpha:
print("The difference is statistically significant (p-value: {:.4f})".format(p_value))
else:
print("The difference is not statistically significant (p-value: {:.4f})".format(p_value))
The difference is statistically significant (p-value: 0.0000)
Il test statistico ĆØ stato condotto per confrontare le anomalie di temperatura derivate dalle osservazioni di ERA5 e dalla simulazione del modello MPI-ESM1-2-LR. Un valore p di 0,0000 indica una forte significativitĆ statistica, sostenendo l'idea che la differenza osservata tra le anomalie di temperatura della superficie nelle osservazioni e nelle simulazioni non ĆØ probabilmente dovuta al caso. Vi ĆØ una robusta evidenza statistica di divergenza tra le temperature osservate e quelle simulate.
Confronto Temperatura Media Aritmetica e Media Pesata¶
Calcolare la media temporale della temperatura di superficie a 2 metri con un approccio pesato basato sulla latitudine è utile per garantire una rappresentazione più equa delle variazioni climatiche globali.
Questo metodo tiene conto dell'importanza relativa di diverse regioni, preservando meglio le tendenze climatiche su scala globale e fornendo una visione più dettagliata delle dinamiche climatiche regionali.
# Compute Weights based on Latitude
weights = np.cos(np.deg2rad(temp_near.lat))
# Create Weighted Dataset
temp_near_w = temp_near.weighted(weights)
# Plot arithmetic vs weighted mean on Temperatures
temp_near_w.mean(['lat', 'lon']).sel({'time': slice('2006', '2016')}).t2m.plot(label = 'Weighted mean')
temp_near.mean(['lat', 'lon']).sel({'time': slice('2006', '2016')}).t2m.plot(label = 'Mean')
plt.xlabel('')
plt.ylabel('Temperature (K)')
plt.legend()
<matplotlib.legend.Legend at 0x296fd6d00>
# Plot arithmetic vs weighted mean on anomalies
temp_near_w.mean(["lon", "lat"]).sel({'time': slice('2006', '2016')}).anomalies.plot(label = 'Weighted mean')
temp_near.mean(['lat', 'lon']).sel({'time': slice('2006', '2016')}).anomalies.plot(label = 'Mean')
plt.xlabel('')
plt.ylabel('Temperature Anomalies (K)')
plt.legend()
<matplotlib.legend.Legend at 0x15c5b0ee0>
Le differenze significative tra la media aritmetica e quella ponderata per i valori delle temperature indicano una sensibilitĆ alla distribuzione geografica dei dati. In particolare, la media ponderata tiene maggiormente conto delle regioni con maggior peso, come ad esempio le aree polari o equatoriali, influenzando il risultato complessivo.
D'altra parte, le differenze più lievi nelle anomalie suggeriscono che, nonostante le discrepanze nei valori assoluti, le due medie convergono maggiormente quando vengono considerate le deviazioni rispetto alle normali. Questo potrebbe indicare che, mentre le condizioni di base differiscono geograficamente, le tendenze o le variazioni rispetto alla normale sono più consistenti tra i due set di dati.
Confronto Medie nel Periodo Comune tra Temperatura e Modelli¶
Temperatura¶
# Compute the Dataset weighted
temp_near_w = temp_near.weighted(weights)
model_w = model[['ts']].weighted(weights)
# Plot the time series comparison
temp_near_w.mean(['lat', 'lon']).sel({'time': slice(start_date_model, end_date_temp)}).t2m.plot(label='Mean Temperature (ERA5 Observations)')
model_w.mean(['lat', 'lon']).sel({'time': slice(start_date_model, end_date_temp)}).ts.plot(label='Mean Temperature (MPI-ESM1-2-LR Simulations)')
plt.xlabel('')
plt.ylabel('Temperature (K)')
plt.legend()
<matplotlib.legend.Legend at 0x174958b80>
Test se le Differenze sono Statisticamente Significative¶
# Calculate the mean over latitude and longitude
mean_obs = temp_near_w.mean(['lat', 'lon']).sel(time=slice(start_date_model, end_date_temp)).t2m
mean_model = model_w.mean(['lat', 'lon']).sel(time=slice(start_date_model, end_date_temp)).ts
# Perform a two-sample t-test
t_stat, p_value = stats.ttest_rel(mean_obs, mean_model)
# Check the p-value to determine significance
alpha = 0.05
if p_value < alpha:
print("The difference is statistically significant (p-value: {:.4f})".format(p_value))
else:
print("The difference is not statistically significant (p-value: {:.4f})".format(p_value))
The difference is statistically significant (p-value: 0.0000)
I valori del p-value ottenuto dal test statistici sono inferiori a 0,05, portando al rifiuto dell'ipotesi nulla e indicando che le differenze osservate sono statisticamente significative. Ciò implica che vi sono disparità tra i dataset confrontati.
Anomalie¶
# Compute the Dataset weighted
temp_near_w = temp_near.weighted(weights)
model_w = model[['anomalies']].weighted(weights)
# Plot the time series comparison
temp_near_w.mean(['lat', 'lon']).sel({'time': slice(start_date_model, end_date_temp)}).anomalies.plot(label='Temperature Anomalies from Normals (ERA5 Observations)')
model_w.mean(['lat', 'lon']).sel({'time': slice(start_date_model, end_date_temp)}).anomalies.plot(label='Temperature Anomalies from Normals (MPI-ESM1-2-LR Simulations)')
plt.xlabel('')
plt.ylabel('Temperature (K)')
plt.legend()
<matplotlib.legend.Legend at 0x1776b2040>
Da questo plot noto che ci sono delle differenze nelle anomalie pesate tra la temperatura osservata e il modello.
L'approccio pesato dà più peso alle variazioni climatiche nelle regioni specifiche, come ad esempio quelle vicino all'equatore o polari, e le discrepanze maggiori potrebbero indicare una risposta diversificata in queste aree.
In altre parole, le variazioni regionali nel modello potrebbero deviare in modo più sostanziale dalle variazioni osservate quando si considera il peso relativo attribuito a ciascuna latitudine nelle anomalie pesate. Questo può essere dovuto a differenze nei parametri del modello o nella rappresentazione delle dinamiche climatiche regionali.
Test se le Differenze sono Statisticamente Significative¶
# Calculate the mean over latitude and longitude
mean_obs = temp_near_w.mean(['lat', 'lon']).sel(time=slice(start_date_model, end_date_temp)).anomalies
mean_model = model_w.mean(['lat', 'lon']).sel(time=slice(start_date_model, end_date_temp)).anomalies
# Perform a two-sample t-test
t_stat, p_value = stats.ttest_rel(mean_obs, mean_model)
# Check the p-value to determine significance
alpha = 0.05
if p_value < alpha:
print("The difference is statistically significant (p-value: {:.4f})".format(p_value))
else:
print("The difference is not statistically significant (p-value: {:.4f})".format(p_value))
The difference is statistically significant (p-value: 0.0000)
I valori del p-value ottenuto dal test statistici sono inferiori a 0,05, portando al rifiuto dell'ipotesi nulla e indicando che le differenze osservate sono statisticamente significative. Ciò implica che vi sono disparità tra i dataset confrontati.
Analisi del Trend con Regressione Lineare¶
# Plot the global anomalies of the 2 datasets
fig, ax = plt.subplots()
temp_near_w.mean(['lat', 'lon']).anomalies.plot(label='ERA5 Observations')
model_w.mean(['lat', 'lon']).anomalies.plot(label='MPI-ESM1-2-LR Simulations')
plt.xlabel('')
plt.ylabel('Temperature Anomalies (K)')
plt.legend()
ax.xaxis.set_major_locator(mdates.YearLocator(10))
plt.axvspan(datetime(1961,1,1), datetime(1990,12,31), ymax=0.02, color="red", alpha=0.3)
<matplotlib.patches.Polygon at 0x129e5e760>
# Plot the the time series with linear regression
# Anomalies
anom1 = temp_near.anomalies.mean(['lat', 'lon'])
anom2 = model.anomalies.mean(['lat', 'lon'])
# Linear regression
x1 = np.arange(temp_near.time.size)
x2 = np.arange(model.time.size)
fit1 = np.polyfit(x1, anom1, 1)
fit2 = np.polyfit(x2, anom2, 1)
fit_func1 = np.poly1d(fit1)
fit_func2 = np.poly1d(fit2)
# Plot time series
fig, ax = plt.subplots()
anom1.plot(label='ERA5 2mt')
anom2.plot(label='MPI-ESM1-2-LR ssp126 2mt')
# Plot linear regression
ax.plot(temp_near.time, fit_func1(x1), color='black', label='Linear regression')
ax.plot(model.time, fit_func2(x2), color='black')
plt.xlabel('')
plt.ylabel('Temperature Anomalies')
plt.legend()
ax.xaxis.set_major_locator(mdates.YearLocator(10))
plt.axvspan(datetime(1961,1,1), datetime(1990,12,31), ymax=0.02, color="red", alpha=0.3)
<matplotlib.patches.Polygon at 0x12aa29670>
Ho eseguito un'analisi del trend delle anomalie utilizzando la regressione lineare, concentrandomi sul confronto tra le anomalie del modello MPI-ESM1-2-LR in scenario ssp126 e le osservazioni di ERA5. Noto che le anomalie del modello iniziano da un valore più elevato rispetto alle anomalie osservate. In generale, emergono differenze nei valori delle anomalie tra il modello e le osservazioni. Inoltre, ho osservato che entrambi mostrano un trend positivo, e questi trend sembrano seguire una traiettoria parallela nel tempo.